package vm.devtool

import com.google.gson.GsonBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import vm.VmService
import vm.element.*
import vm.getInstance
import vm.getObject
import vm.getObjectWithClassObj
import vm.logging.Logging

/**
 * 检查是否是控制流异常（不应该被记录的异常）
 */
internal fun isControlFlowException(e: Throwable): Boolean {
    val className = e.javaClass.name
    return className.contains("CancellationException") || 
           className.contains("LeftCompositionCancellationException") ||
           e is kotlinx.coroutines.CancellationException
}

data class ProviderNode(
    val id: String,
    val type: String
) {

    fun getProviderPath(): InstancePath.FromProviderId {
        return InstancePath.FromProviderId(id)
    }
}


/**
 * 表示属性的路径，用于跟踪展开状态。
 * 使用 data class 来获得 equals/hashCode 的自动实现。
 */
sealed class PathToProperty {
    data class ListIndex(val index: Int) : PathToProperty()
    data class MapKey(val ref: String? = null) : PathToProperty()
    data class ObjectProperty(val name: String, val ownerUri: String, val ownerName: String, val field: ObjectField) :
        PathToProperty()
}


/**
 * 表示从根对象到一个特定属性的完整路径。
 */
sealed class InstancePath {
    abstract val pathToProperty: List<PathToProperty>
    abstract fun copy(pathToProperty: List<PathToProperty>): InstancePath
    data class FromInstanceId(
        val instanceId: String,
        override val pathToProperty: List<PathToProperty> = emptyList()
    ) : InstancePath() {
        override fun copy(pathToProperty: List<PathToProperty>): InstancePath {
            return this.copy(instanceId = this.instanceId, pathToProperty = pathToProperty)
        }
    }

    data class FromProviderId(
        val providerId: String,
        override val pathToProperty: List<PathToProperty> = emptyList()
    ) : InstancePath() {
        override fun copy(pathToProperty: List<PathToProperty>): InstancePath {
            return this.copy(providerId = this.providerId, pathToProperty = pathToProperty)
        }
    }

    /**
     * 创建一个新的路径，该路径指向当前路径下的一个子属性。
     */
    fun pathForChild(property: PathToProperty): InstancePath {
        val newPath = pathToProperty + property
        return this.copy(pathToProperty = newPath)
    }

    fun pathForChildWithInstance(property: PathToProperty, instanceId: String): FromInstanceId {
        val newPath = pathToProperty + property
        return FromInstanceId(instanceId = instanceId, newPath)
    }


}


suspend fun ObjectField.getFieldInstance(vmService: VmService, parentInstance: InstanceDetails.Object): Instance? {
    if (isStatic) return null
    val eval: EvalOnDartLibrary = this.eval
    val expression = "(parent as ${this.ownerName}).${this.name}"
    val instanceRef = eval.safeEval(
        vmService.getMainIsolateId(),
        expression,
        mapOf("parent" to parentInstance.instanceRefId)
    )
    val instance = vmService.getObject(
        vmService.getMainIsolateId(),
        instanceRef.getId()
    )
    return instance
}

/**
 * 封装一个对象的字段信息。
 */
data class ObjectField(
    val name: String,
    val isFinal: Boolean,
    val ownerName: String,
    val ownerUri: String,
    val ref: InstanceRef,
    @Transient
    val eval: EvalOnDartLibrary,
    val isDefinedByDependency: Boolean,
    val isStatic: Boolean,
) {
    override fun toString(): String {
        return GsonBuilder().setPrettyPrinting().create().toJson(this)
    }

    val isPrivate get() = name.startsWith("_")

    fun createEval(vm: VmService): EvalOnDartLibrary = EvalOnDartLibrary("dart:io", vm)
    fun createEvalWithOwner(vm: VmService): EvalOnDartLibrary = EvalOnDartLibrary(ownerUri, vm)

    suspend fun getInstance(vm: VmService, parentInstance: InstanceDetails.Object): Instance? {
        return this.getFieldInstance(vm, parentInstance)
    }
}

/**
 * 封装不同类型实例的详细信息，用于UI展示。
 * 这是一个 sealed class，类似于 Dart 中的 freezed union type。
 */
sealed class InstanceDetails {
    data object Nill : InstanceDetails()
    data class Bool(val displayString: String, val instanceRefId: String) : InstanceDetails()
    data class Number(val displayString: String, val instanceRefId: String) : InstanceDetails()
    data class DartString(val displayString: String, val instanceRefId: String) : InstanceDetails()
    data class Enum(val type: String, val value: String, val instanceRefId: String) : InstanceDetails()

    data class DartList(val length: Int, val hash: Int, val instanceRefId: String, val elements: List<InstanceRef>) :
        InstanceDetails()

    data class Map(
        val associations: List<MapAssociation>, // 存储键值对的引用
        val hash: Int,
        val instanceRefId: String
    ) : InstanceDetails()

    data class Object(
        val type: String,
        val fields: List<ObjectField>, // 注意，这里的 List 来自 kotlin.collections
        val hash: Int,
        val instanceRefId: String,
        @Transient
        val evalForInstance: EvalOnDartLibrary // 每个对象实例都关联一个在其库上下文中执行的 eval
    ) : InstanceDetails() {
        val fieldsFiltered get() = fields.filter { it.isDefinedByDependency.not() }.filter { it.isStatic.not() }
        override fun toString(): String {
            return "Object: ${type}, fields: ${fields.joinToString(", ")} instance ref id : ${instanceRefId}"
        }
    }

    data class MapEntry(val key: InstanceDetails, val value: InstanceDetails)

    val isExpandable: Boolean
        get() = when (this) {
            is DartList -> length > 0
            is Map -> associations.isNotEmpty()
            is Object -> fields.isNotEmpty()
            else -> false
        }

    override fun toString(): String {
        return GsonBuilder().setPrettyPrinting().create().toJson(this)
    }
}

object ProviderHelper {

    /**
     * 修改对象字段的值
     * @param vm VmService
     * @param parentInstanceId 父对象的实例ID
     * @param field 要修改的字段
     * @param newValue 新值
     * @param triggerNotify 是否触发 notifyListeners (针对 ChangeNotifier)
     * @return 是否修改成功
     */
    suspend fun updateFieldValue(
        vm: VmService,
        parentInstanceId: String,
        field: ObjectField,
        newValue: String,
        triggerNotify: Boolean = true
    ): Boolean {
        return try {
            val mainIsolateId = vm.getMainIsolateId()
            val eval = field.eval
            
            // 构建赋值表达式
            val expression = "(parent as ${field.ownerName}).${field.name} = $newValue"
            
            eval.safeEval(
                mainIsolateId,
                expression,
                mapOf("parent" to parentInstanceId)
            )
            
            // 如果需要，触发 notifyListeners
            if (triggerNotify) {
                try {
                    // 尝试调用 notifyListeners (如果对象是 ChangeNotifier)
                    val notifyExpression = "(parent as dynamic).notifyListeners()"
                    eval.safeEval(
                        mainIsolateId,
                        notifyExpression,
                        mapOf("parent" to parentInstanceId)
                    )
                    Logging.getLogger().logInformation("Triggered notifyListeners for ${field.name}")
                } catch (e: Exception) {
                    // 如果对象不是 ChangeNotifier，忽略错误
                    Logging.getLogger().logInformation("Object is not a ChangeNotifier, skipping notifyListeners")
                }
            }
            
            Logging.getLogger().logInformation("Successfully updated field ${field.name} to $newValue")
            true
        } catch (e: Exception) {
            // 不记录控制流异常
            if (!isControlFlowException(e)) {
                Logging.getLogger().logError("Failed to update field ${field.name}", e)
            }
            false
        }
    }

    suspend fun getProviderNodes(vm: VmService): List<ProviderNode> {
        val mainIsolateId = vm.getMainIsolateId()
        val providerEval = EvalOnDartLibrary("package:provider/src/provider.dart", vm)
        val instanceRef = providerEval.safeEval(
            mainIsolateId,
            "ProviderBinding.debugInstance.providerDetails.keys.toList()"
        )
        val instance = providerEval.getInstance(mainIsolateId, instanceRef)
        val elements = instance.getElements() ?: return emptyList()
        suspend fun getNode(idRef: InstanceRef): ProviderNode? {
            val providerIdInstance = providerEval.getInstance(mainIsolateId, idRef)
            val id = providerIdInstance.getValueAsString() ?: return null
            val node = providerEval.safeEval(
                mainIsolateId,
                "ProviderBinding.debugInstance.providerDetails['${id}']"
            )
            val nodeInstance = providerEval.getInstance(mainIsolateId, node)
            val fields = nodeInstance.getFields()
            val typeField = fields?.first { it.getDecl()?.getName() == "type" }?.getValue()
            if (typeField != null) {
                val type = providerEval.getInstance(mainIsolateId, typeField).getValueAsString() ?: return null
                return ProviderNode(id, type)
            }
            return null
        }

        return withContext(Dispatchers.IO) {
            elements.map { getNode(it) }
        }.filterNotNull()
    }

    /**
     * 根据给定的路径获取一个实例的【单层】详细信息。
     * 它不再递归，只解析由 path 指定的那个对象。
     */
    suspend fun getInstanceDetails(
        vm: VmService,
        path: InstancePath,
        parent: InstanceDetails? = null
    ): InstanceDetails {
        println("DEBUG: ProviderHelper.getInstanceDetails called with path: $path")
        val mainIsolateId = vm.getMainIsolateId()
        val coreEval = EvalOnDartLibrary("dart:core", vm)
        println("DEBUG: ProviderHelper.getInstanceDetails - got mainIsolateId: $mainIsolateId")
        val currentRef: InstanceRef? = if (parent == null) when (path) {
            is InstancePath.FromProviderId -> {
                val providerEval = EvalOnDartLibrary("package:provider/src/provider.dart", vm)
                println("DEBUG: ProviderHelper.getInstanceDetails - evaluating provider value for ID: ${path.providerId}")
                val result = providerEval.safeEval(
                    mainIsolateId,
                    "ProviderBinding.debugInstance.providerDetails[\"${path.providerId}\"]?.value"
                )
                println("DEBUG: ProviderHelper.getInstanceDetails - got provider value ref: $result")
                result
            }

            is InstancePath.FromInstanceId -> {
                Logging.getLogger().logInformation("获取实例详情")
                val dartEval = EvalOnDartLibrary("dart:io", vm)
                dartEval.safeEval(vm.getMainIsolateId(), "value", mapOf("value" to path.instanceId))
            }
        } else when (parent) {



            is InstanceDetails.Map -> {
                val keyPath = path.pathToProperty.last() as PathToProperty.MapKey
                val key = if (keyPath.ref == null) "null" else "key"
                val keyPathRef = keyPath.ref

                val scope = mutableMapOf("parent" to parent.instanceRefId)
                if (keyPathRef != null) {
                    scope["key"] = keyPathRef
                }
                coreEval.safeEval(mainIsolateId, "parent[$key]", scope)
            }

            is InstanceDetails.Bool -> {
                null
            }

            is InstanceDetails.Enum -> {
                // Enums are leaf nodes, no further navigation needed
                null
            }

            is InstanceDetails.Nill -> {
                null
            }

            is InstanceDetails.Number -> {
                null
            }

            is InstanceDetails.Object -> {
                val propertyPath = path.pathToProperty.last() as PathToProperty.ObjectProperty
                val field = parent.fields.first {
                    it.name == propertyPath.name
                            && it.ownerName == propertyPath.ownerName && it.ownerUri == propertyPath.ownerUri
                }
                Logging.getLogger().logInformation("获取字段详情")
//                field.getFieldInstance(vm, parent)
                field.ref
            }

            is InstanceDetails.DartList -> {
                val indexPath = path.pathToProperty.last() as PathToProperty.ListIndex
                coreEval.safeEval(mainIsolateId, "parent[${indexPath.index}]", mapOf("parent" to parent.instanceRefId))
            }

            is InstanceDetails.DartString -> {
                null
            }
        }

        if (currentRef == null) {
            throw RuntimeException("无法获取实例")
        }


        println("DEBUG: ProviderHelper.getInstanceDetails - getting instance for ref: $currentRef")
        val instance = coreEval.getInstance(mainIsolateId, currentRef)
        println("DEBUG: ProviderHelper.getInstanceDetails - got instance: $instance")
        return instanceToDetails(instance, vm, mainIsolateId)
    }


    private suspend fun instanceToDetails(
        instance: Instance,
        vm: VmService,
        isolateId: String
    ): InstanceDetails {
        val instanceRefId = instance.getId()
        val hash = instance.getIdentityHashCode()

        return when (instance.getKind()) {
            InstanceKind.String -> InstanceDetails.DartString(instance.getValueAsString() ?: "", instanceRefId)
            InstanceKind.Bool -> InstanceDetails.Bool(instance.getValueAsString() ?: "false", instanceRefId)
            InstanceKind.Int, InstanceKind.Double -> InstanceDetails.Number(
                instance.getValueAsString() ?: "0",
                instanceRefId
            )

            InstanceKind.Null -> InstanceDetails.Nill
            InstanceKind.List -> InstanceDetails.DartList(
                length = instance.getLength(),
                hash = hash,
                instanceRefId = instanceRefId,
                elements = instance.getElements()?.toList() ?: emptyList()
            )

            InstanceKind.Map -> {
                val associations = instance.getAssociations()?.map { it } ?: emptyList()
                InstanceDetails.Map(associations, hash, instanceRefId)
            }

            InstanceKind.PlainInstance -> {
                // Check if this is an enum
                val enumValue = instance.getEnumValue()
                if (enumValue != null) {
                    val enumType = instance.getEnumType() ?: "Unknown"
                    InstanceDetails.Enum(
                        type = enumType,
                        value = enumValue,
                        instanceRefId = instanceRefId
                    )
                } else {
                    // Regular object
                    buildObjectDetails(instance, vm, isolateId, instanceRefId, hash)
                }
            }

            else -> {
                buildObjectDetails(instance, vm, isolateId, instanceRefId, hash)
            }
        }
    }

    private suspend fun buildObjectDetails(
        instance: Instance,
        vm: VmService,
        isolateId: String,
        instanceRefId: String,
        hash: Int
    ): InstanceDetails.Object {
        val classRef = instance.getClassRef()
        val libraryUri = classRef.getLibrary()?.getUri() ?: "dart:core"
        val evalForInstance = EvalOnDartLibrary(libraryUri, vm)
        val allFields = mutableListOf<ObjectField>()

        val objectInstance = vm.getInstance(isolateId, instanceRefId)
        objectInstance?.getFields()?.forEach { field ->
            try {
                val decl = field.getDecl() ?: return@forEach
                val ref = field.getValue() ?: return@forEach
                
                // 检查是否是 Sentinel（过期的引用）
                val refType = ref.type
                if (refType == "Sentinel") {
                    // 跳过已过期的引用
                    Logging.getLogger().logInformation("Skipping expired field: ${field.getName()}")
                    return@forEach
                }
                
                val owner: ClassObj? = vm.getObjectWithClassObj(isolateId, classRef.getId())
                val ownerName: String = (owner?.getMixin()?.getName() ?: owner?.getName()) ?: return@forEach
                val ownerUri: String = decl.getLocation()?.getScript()?.getUri() ?: return@forEach
                val ownerPackageName: String? = tryParsePackageName(ownerUri)
                val isolate: Isolate = vm.getIsolateByIdPub(vm.getMainIsolates()?.getId() ?: return@forEach)
                    ?: return@forEach
                val appName: String? = tryParsePackageName(isolate.getRootLib()?.getUri() ?: return@forEach)

                val fieldObject = ObjectField(
                    name = field.getName() ?: "",
                    isFinal = decl.isFinal(),
                    isStatic = decl.isStatic(),
                    eval = EvalOnDartLibrary(ownerUri, vm),
                    ref = ref,
                    ownerUri = ownerUri,
                    isDefinedByDependency = ownerPackageName != appName,
                    ownerName = ownerName,
                )
                allFields.add(fieldObject)
            } catch (e: Exception) {
                // 检查是否是 Sentinel 相关的异常
                if (e.message?.contains("Sentinel") == true || e.message?.contains("Expired") == true) {
                    Logging.getLogger().logInformation("Skipping expired field: ${field.getName()}")
                    return@forEach
                }
                // 不记录控制流异常（如 CancellationException）
                if (!isControlFlowException(e)) {
                    Logging.getLogger().logError("Error processing field: ${field.getName()}", e)
                }
            }
        }

        return InstanceDetails.Object(
            type = classRef.getName(),
            fields = allFields.distinctBy { it.name }.sortedBy { it.name },
            hash = hash,
            instanceRefId = instanceRefId,
            evalForInstance = evalForInstance
        )
    }
}

private fun tryParsePackageName(uri: String): String? {
    return Regex("package:(.+?)/").find(uri)?.groupValues?.get(1)
}
